<!--
  数据文件合并：asc文件转为xlsx文件
 -->

<!--
  视图层
-->
<template><header-comp />

<!-- 功能简介 -->
<card-comp title="功能简介与实现">
  <!-- 通告栏 -->
  <uni-notice-bar text=
    "To新手：
    　　用上一步下载的.pgm脚本处理.sif文件得到.asc文件。然后，在本页面直接读取含.asc数据的文件夹即可。已经做好.sif等其它无关文件的过滤排除功能了。"
  />
  <!-- 二级标题：功能实现 -->
  <section-comp title="功能实现">
    <!-- 文字内容 -->
    <view>
    　　先读取含有.asc数据文件的文件夹，然后点击导出excel。
    </view>
    <!-- 读取数据文件夹按钮及容器 -->
    <view class="center">
      <button
        :disabled="isGetFoldRef"
        @click="readDataDirectory()"
        size="mini" type="primary"
      >
        {{ isGetFoldRef ? `获得有效数据文件${ fileCountsRef }个` : "挂载文件夹" }}
      </button>
    </view>
    <!-- 导出excel -->
    <view
      v-if="isGetFoldRef"
      class="center"
    >
      <button
        :disabled="!isGetFoldRef"
        @click="ascsToXlsx()"
        size="mini" type="warn"
      >
        一键导出excel
      </button>
    </view>
  </section-comp>
  <!-- 二级标题：功能简介 -->
  <section-comp title="功能简介">
    <!-- 文字内容 -->
    <view>
    　　本来想“一站式”解决问题的，但Andor的sif文件有自己的编码，外部解码转码的工作量很大，所以用Andor自己提供的脚本接口来实现转码，也就是.pgm脚本文件。但是Andor脚本接口是闭源的，功能实现非常有限，所以又写了本页面，来实现解码转码的.asc文件一键汇总为excel表格的功能。<br />
    　　前一步的.pgm脚本文件功能很简单，就是读取.sif，另存.asc。可以用文本文档打开.pgm脚本文件查看注释或修改脚本。<br />
    　　本页面功能也很简单，挂载文件夹之后，获取文件夹内的文件句柄，根据文件扩展名过滤。然后就是以句柄获取文件内容 + 转为excel格式等操作。没啥参数需要调的。<br />
    </view>
  </section-comp>
</card-comp>

<!-- 页脚(下) -->
<footer-comp /></template>

<!-- 逻辑层 -->
<script setup>
// 导入vue的框架方法
import { shallowRef } from "vue"
// 引入各类方法
import { aoaTranspose, aoaMapToWorkbook, downloadXlsx } from "@/assets/app-xlsx.js"

/** Ref状态：是否读取到文件夹 */
const isGetFoldRef = shallowRef(false)
/** Ref状态：读取到的有效文件数量 */
const fileCountsRef = shallowRef(0)

/**
 * 数据文件合并项目的数据对象
 * @type { Object } projectObj
 * @type { String[] } projectObj.fileNameArr - (有效)文件名数组
 * @type { FileSystemFileHandle[] } projectObj.fileHandleArr - 文件句柄数组
 */
const projectObj = {
  fileNameArr: null,
  fileHandleArr: null
}

/**
 * 读取(.asc)数据文件夹
 * 内容为各类.asc文件，但是可能混有别的文件，所以要筛
 * @note 会读写projectObj的fileNameArr和fileHandleArr
 */
async function readDataDirectory() { try {
  // 显示加载圈
  await uni.loading("正在读取并校验文件数据")
  // 先清零旧的projectObj.file数据
  projectObj.fileNameArr = null
  projectObj.fileHandleArr = null
  // 建个空数组，用来装文件名和句柄内容
  const fileNameArr = []
  const fileHandleArr = []
  // 打开文件夹，返回句柄。句柄成员对象：kind、name
  const dirHandle = await window.showDirectoryPicker()
  // 处理句柄，得到异步迭代器
  const asyncIter = dirHandle.entries()
  // 异步迭代
  forEachFile: for await (const [fileName, fileHandle] of asyncIter) {
    // 按“.”拆分，提取最后一个，即为文件扩展名
    const fileNameExt = fileName.split(".").slice(-1)[0]
    // 检查扩展名是否是"asc"
    if (fileNameExt != "asc") {
      // 不是，则直接跳过本次遍历
      continue forEachFile
    }
    // 是，则构造文件名。先看看前一个扩展名有没有“sif”
    const fileNameSecExt = fileName.split(".").slice(-2, -1)[0]
    // 有则去掉sif，没有就不用去了，然后合并拼凑文件名
    const fileNamePure = (fileNameSecExt === "sif")
      ? fileName.split(".").slice(0, -2).join(".")
      : fileName.split(".").slice(0, -1).join(".")
    // 推入文件名数组
    fileNameArr.push(fileNamePure)
    // 文件句柄也推进句柄数组
    fileHandleArr.push(fileHandle)
  }
  // 搞定，把文件对象输出到项目全局对象
  projectObj.fileNameArr = fileNameArr
  projectObj.fileHandleArr = fileHandleArr
  // 更新Ref状态
  const fileNameArrLength = fileNameArr.length
  fileCountsRef.value = fileNameArrLength
  isGetFoldRef.value = true
  // 隐藏加载圈
  uni.hideLoading()
  // 提示成功
  uni.showModal({
    showCancel: false,
    title: "读取成功",
    content: `获得有效数据文件 ${ fileNameArrLength } 个，可一键导出。`,
  })
} catch (error) {
  console.error("readDataDirectory()报错: ", error)
  uni.hideLoading()
  uni.showModal({
    showCancel: false,
    title: "读取数据报错",
    content: error.message,
  })
}}

/**
 * 读取(.asc)数据文件夹
 * 内容为各类.asc文件，但是可能混有别的文件，所以要筛
 */
async function ascsToXlsx() { try {
  // 显示加载圈
  await uni.loading("正在转换文件数据")
  // 从项目全局接文件对象
  const fileNameArr = projectObj.fileNameArr
  const fileHandleArr = projectObj.fileHandleArr
  // 新建一个数组用来装数据
  const fileDataAoa = []
  // 遍历文件
  for (let i = 0; i < fileNameArr.length; i++) {
    // 从句柄获取文件
    const file = await fileHandleArr[i].getFile()
    // 从文件获取text数据
    const fileText = await file.text()
    // 按换行拆分成数组
    const lineBreakReg = new RegExp(/\r?\n/)
    const fileTextArr = fileText.split(lineBreakReg)
    // 如果数组最后一个元素为空，则删掉
    if (!fileTextArr.slice(-1)[0]) {
      fileTextArr.splice(-1)
    }
    // 准备拆分数组内的tab空格
    const tabReg = new RegExp(/\t/)
    // 新建一个AOA数组用来装每个文件的数据
    // 第一个Arr是X轴数据，先把X装进去
    const fileDataUniAoa = []
    // 这里需要注意，有的文件可能只有1列Y数据，还有的文件可能有多列Y数据
    // 所以要判断一下，用第一行数据来判断
    const fileDataFristLineArr = fileTextArr[0].split(tabReg)
    // 如果数组最后一个元素为空，则删掉
    if (!fileDataFristLineArr.slice(-1)[0]) {
      fileDataFristLineArr.splice(-1)
    }
    // 装X
    fileDataUniAoa[0] = ["X", fileDataFristLineArr[0]]
    // 遍历装Y数据
    for (let k = 1; k < fileDataFristLineArr.length; k++) {
      // 装Y
      fileDataUniAoa[k] = [fileNameArr[i], fileDataFristLineArr[k]]
    }
    // 第一行搞定，数据结构也搞定了，接下来继续处理
    // 遍历文件Text化的字符串数组
    for (let j = 1; j < fileTextArr.length; j++) {
      // 按tab拆分成数组，内容就是X和Y数据
      const fileDataUniArr = fileTextArr[j].split(tabReg)
      // 遍历装X和Y数据
      for (let k = 0; k < fileDataUniAoa.length; k++) {
        // 装X和Y
        fileDataUniAoa[k].push(fileDataUniArr[k])
      }
    }
    // 数据数组装箱
    fileDataAoa.push(...fileDataUniAoa)
  }
  // 处理完毕，构造AoaMap
  const fileDataAoaMap = new Map()
  // Key是“rawData”，Value是转置后的原始数据
  fileDataAoaMap.set("rawData", aoaTranspose(fileDataAoa))
  // 构造Excel工作簿
  const workbook = aoaMapToWorkbook(fileDataAoaMap)
  // 隐藏加载圈
  uni.hideLoading()
  // 下载Excel文件
  downloadXlsx(workbook, "andor-data.xlsx")
} catch (error) {
  console.error("ascsToXlsx()报错: ", error)
  uni.hideLoading()
  uni.showModal({
    showCancel: false,
    title: "读取数据报错",
    content: error.message,
  })
}}

</script>
